package yxy.banana.socket.controlled;

import com.google.protobuf.ByteString;

import yxy.apple.protobuf.Message.Parcal;
import yxy.banana.socket.NetworkElement;

public abstract class Controller<H extends ControlledHandler> {

	public static enum DroppedReason {
		Unknown, BadFormat, BadVersion, BadContent, Unnoticed, UnknownProto,
	}

	private boolean bound = false;

	private H handler = null;

	public static <H extends ControlledHandler> H bind(Controller<H> controller, Class<H> clazz) throws Exception {
		H instance = clazz.getConstructor().newInstance();
		controller.bind(instance);
		return instance;
	}

	public int bind(H handler) throws Exception {
		if (!this.bound && handler.channel() == 0) {
			// 这里存在一个双向引用，在close时会被解除掉
			int channel = this.manager().add(handler);
			handler.setChannel(channel);
			handler.setController(this);
			this.handler = handler;
			return channel;
		} else {
			String description = String.format("Can not bind with this handler:%s", handler.toString());
			throw new RuntimeException(description);
		}
	}

	public H handler() {
		return this.handler;
	}

	/*
	 * **************************************** 回调
	 * ********************************************
	 */

	public void open() {
		this.opened();
	}

	protected abstract void opened();

	public void close() {
		this.closed();
		if (this.handler != null) {
			this.manager().remove(this.handler.channel());
			this.handler = null;
		}
	}

	protected abstract void closed();

	public void active() {
		this.actived();
	}

	protected abstract void actived();

	public void inactive() {
		this.inactived();
	}

	protected abstract void inactived();

	public void trigger(Object event) {
		this.triggered(event);
	}

	protected abstract void triggered(Object event);

	public void caught(Throwable cause) {
		this.thrown(cause);
	}

	protected abstract void thrown(Throwable cause);

	public void drop(Object message, DroppedReason reason) {
		this.dropped(message, reason);
	}

	protected abstract void dropped(Object message, DroppedReason reason);

	protected abstract boolean control(Parcal parcal) throws Exception;

	@Override
	public String toString() {
		return String.format("{class:\"%s\", handler:%s}", this.getClass().getSimpleName(), this.handler);
	}

	protected String toString(int sequence, String tag, long source, long destination, int proto, ByteString content) {
		StringBuffer buffer = new StringBuffer();
		buffer.append("{");
		buffer.append("\r\n\tsequence: ");
		buffer.append(sequence);
		buffer.append("\r\n\ttag: ");
		buffer.append(tag);
		buffer.append("\r\n\tsource: ");
		buffer.append(NetworkElement.assemble(source));
		buffer.append("\r\n\tdestination: ");
		buffer.append(NetworkElement.assemble(destination));
		buffer.append("\r\n\tcontrol: {");
		buffer.append("\r\n\t\tproto: ");
		buffer.append(proto);
		buffer.append("\r\n\t\tdata: ");
		buffer.append(content);
		buffer.append("\r\n\t}");
		buffer.append("\r\n}");
		return buffer.toString();
	}

	protected abstract ChannelManager<H> manager();
}
